<template>
  <div>
    <div v-for="(node, index) in props.treeData" :key="index" class="node-item">
      <div class="node-wrap">
        <div class="menu-wrap">
          <div class="arrow" @click="handleToggleExpand(node.id)">
            <template v-if="node.children && node.children.length > 0">
              <CaretBottom
                style="width: 1.3em; height: 1.3em"
                :class="`${state.closeNodeIds.indexOf(node.id) === -1 ? 'expend' : 'close'}`"
              />
            </template>
          </div>
          <el-checkbox
            v-model="node.checked"
            :disabled="!auth"
            :indeterminate="node.indeterminate"
            class="checkbox"
            @change="handleChangeNode($event, node)"
          ></el-checkbox>
          <div>{{ node.label }}</div>
          <div v-if="!node.children || node.children.length === 0">
            <span v-show="auth">
              <span class="only-menu" @click="handleCheckOnlyNode(node)">仅授权菜单</span>
            </span>
          </div>
        </div>
        <div v-if="node.funcKeys.length > 0" class="func-wrap">
          <el-button
            v-for="(f, i) in node.funcKeys"
            size="small"
            :key="i"
            :disabled="!auth"
            :type="`${f.checked ? 'primary' : 'default'}`"
            :plain="!f.checked"
            @click="handleClickFunc(f, node)"
          >
            {{ f.label }}
          </el-button>
        </div>
      </div>
      <div v-if="node.children && node.children.length > 0">
        <el-collapse-transition>
          <MenuTree
            :disabled="props.disabled"
            v-show="state.closeNodeIds.indexOf(node.id) === -1"
            :style="`padding-left: ${(props.level + 1) * 16}px`"
            :level="props.level + 1"
            :tree-data="node.children"
            :parent-node="node"
            @trace="handleTraceParent"
          ></MenuTree>
        </el-collapse-transition>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, computed } from 'vue'
import { ElCheckbox, ElCollapseTransition } from 'element-plus'
import { CaretBottom } from '@element-plus/icons-vue'
import { useAuth } from '@/hooks/auth'
import type { TreeMenu, FuncKey } from './type'

interface TraceNode {
  traceNode: TreeMenu
  parent: TreeMenu
  trueCount: number
  falseCount: number
  totalCount: number
}

const props = withDefaults(
  defineProps<{
    disabled: boolean
    treeData?: TreeMenu[]
    authData?: any
    parentNode?: TreeMenu
    level: number
  }>(),
  {
    disabled: false,
    treeData: () => [] as TreeMenu[],
    authData: () => {},
    parentNode: undefined,
    level: 1
  }
)

const emits = defineEmits(['trace', 'auth'])

const { hasFuncKey } = useAuth()

const state = reactive<{ closeNodeIds: string[]; parentNodes: { [key: string]: TreeMenu | null } }>({
  closeNodeIds: [], // // 展开的nodeId;
  parentNodes: {} // 记录每个节点的父节点 id:parent
})

// 权限
const auth = computed(() => hasFuncKey('Auth') && !props.disabled)

const handleToggleExpand = (id: string) => {
  const index = state.closeNodeIds.indexOf(id)
  if (index === -1) {
    state.closeNodeIds.push(id)
  } else {
    state.closeNodeIds.splice(index, 1)
  }
}
const handleClickFunc = (func: FuncKey, node: TreeMenu) => {
  func.checked = !func.checked

  //点击func和点击仅授权菜单, 处理逻辑是一样的
  handleCheckOnlyNode(node)
}
const handleCheckOnlyNode = (node: TreeMenu, endToSave = true) => {
  //点击仅授权菜单, 不向下及联功能按钮
  //获得当前节点的funcKeys兄弟trueCount, falseCount
  let { trueCount, falseCount, totalCount } = getSiblingTrueFalse(node.funcKeys)
  //处理当前node的
  setIndeterminate(node, { trueCount, falseCount, totalCount })

  if (trueCount === 0 && totalCount > 0) {
    //在处理末级菜单时, 下级func都是未checked, 此时经过上面this.setIndeterminate处理indeterminate一定是false
    //但是在此需要强制实现indeterminate=true效果
    node.indeterminate = true
    trueCount++
    falseCount--
  }
  //只要是勾末级菜单的,就一定赋值true
  node.checked = true

  if (node.parentId !== '') {
    //再获得当前node兄弟的trueCount, falseCount, 累加
    //this.treeData在当前的子组件中已经是兄弟list了, 不是最原始的treeData
    const nodeCount = getSiblingTrueFalse(props.treeData)
    nodeCount.totalCount = nodeCount.totalCount + totalCount
    nodeCount.falseCount = nodeCount.falseCount + falseCount
    nodeCount.trueCount = nodeCount.trueCount + trueCount
    //向上及联
    emits('trace', { traceNode: node, parent: props.parentNode, ...nodeCount })
  } else {
    if (endToSave) {
      //已经到顶级, 开始保存node, 只从初始点击的节点向下递归遍历即可
      saveCheckedItem(node)
    }
  }
}
const handleChangeNode = ($event: any, node: TreeMenu) => {
  // 向下及联
  checkChildren(node, $event)
  // 向上及联
  checkParent(node)
}
const checkChildren = (node: TreeMenu, $event: any) => {
  node.indeterminate = false
  //子节点递归处理
  if (node.children) {
    for (let i = 0; i < node.children.length; i++) {
      const child = node.children[i]
      child.checked = $event
      checkChildren(child, $event)
    }
  }
  //funcKeys功能按钮同样当作节点处理
  if (node.funcKeys) {
    for (let i = 0; i < node.funcKeys.length; i++) {
      const func = node.funcKeys[i]
      func.checked = $event
    }
  }
}

const checkParent = (node: TreeMenu) => {
  //初始点击的节点, 从这里开始向上追溯
  if (node.parentId !== '') {
    //获得当前节点的兄弟trueCount, falseCount,
    //this.treeData在当前的子组件中已经是兄弟list了, 不是最原始的treeData
    const nodeCount = getSiblingTrueFalse(props.treeData)
    //向上追溯到最顶级
    emits('trace', { traceNode: node, parent: props.parentNode, ...nodeCount })
  } else {
    //已经到顶级, 开始保存node, 只从初始点击的节点向下递归遍历即可
    saveCheckedItem(node)
  }
}

const handleTraceParent = (child: TraceNode) => {
  //先处理当前节点自己的状态, child.parent
  const node = child.parent

  //child中包括下级节点通过trace传上来的...nodeCount
  setIndeterminate(node, child)

  if (node.parentId !== '') {
    //获得当前节点的兄弟trueCount, falseCount
    //this.treeData在当前的子组件中已经是兄弟list了, 不是最原始的treeData
    const { trueCount, falseCount, totalCount } = getSiblingTrueFalse(props.treeData)

    //累加
    child.totalCount = child.totalCount + totalCount
    child.trueCount = child.trueCount + trueCount
    child.falseCount = child.falseCount + falseCount

    //父节点已经改变
    child.parent = props.parentNode

    //向上追溯到最顶级
    emits('trace', child)
  } else {
    //已经到顶级, 开始保存node, 只从初始点击的节点向下递归遍历即可
    saveCheckedItem(child.traceNode)
  }
}

// 统计兄弟节点的TreeFalse
const getSiblingTrueFalse = (array: FuncKey[]) => {
  if (array && array.length > 0) {
    let trueCount = 0
    let falseCount = 0
    for (let i = 0; i < array.length; i++) {
      const sibling = array[i]
      if (sibling.checked) {
        trueCount++
      } else {
        falseCount++
      }
    }
    return { trueCount, falseCount, totalCount: array.length }
  } else {
    return { trueCount: 0, falseCount: 0, totalCount: 0 }
  }
}

// 处理node不确定状态
const setIndeterminate = (node: TreeMenu, count: any) => {
  if (count.totalCount === count.trueCount) {
    // falseCount == 0
    node.checked = true
    node.indeterminate = false
  } else if (count.totalCount === count.falseCount) {
    //trueCount === 0
    node.checked = false
    node.indeterminate = false
  } else {
    // trueCount > 0 && falseCount > 0
    node.checked = false
    node.indeterminate = true
  }
}

// 触发保存checked节点的事件
const saveCheckedItem = (node: TreeMenu) => {
  // log.action('授权')
  const array = getChildren(node)
  emits('auth', array)
}

// 向下递归获取所有checked的末级node和func
const getChildren = (
  child: TreeMenu,
  trueNodeIds: string[] = [],
  trueFuncKeys: string[] = [],
  falseNodeIds: string[] = [],
  falseFuncKeys: string[] = []
) => {
  if (child.children && child.children.length > 0) {
    for (let i = 0; i < child.children.length; i++) {
      const node = child.children[i]
      getChildren(node, trueNodeIds, trueFuncKeys, falseNodeIds, falseFuncKeys)
    }
  } else {
    if (child.checked) {
      trueNodeIds.push(child.id)
    } else {
      falseNodeIds.push(child.id)
    }
  }
  //当前节点的funcKeys
  if (child.funcKeys) {
    for (let i = 0; i < child.funcKeys.length; i++) {
      const func = child.funcKeys[i]
      if (func.checked) {
        trueFuncKeys.push(func.id)
      } else {
        falseFuncKeys.push(func.id)
      }
    }
  }
  return { trueNodeIds, trueFuncKeys, falseNodeIds, falseFuncKeys }
}

const updateStatus = (ids: string[], funcKeys: string[]) => {
  // 初始所有节点false
  updateAllFalse(props.treeData)

  //先更新末级checked
  const updatedNodes = updateChecked(props.treeData, null, ids, funcKeys)

  // updatedNodes全部都是末级节点
  for (let i = updatedNodes.length - 1; i >= 0; i--) {
    const node = updatedNodes[i]
    //只有末级菜单node才会走这个处理
    let { trueCount, falseCount, totalCount } = getSiblingTrueFalse(node.funcKeys)
    setIndeterminate(node, { trueCount, falseCount, totalCount })
    if (trueCount === 0 && totalCount > 0) {
      //在处理末级菜单时, 下级func都是未checked, 此时经过上面this.setIndeterminate处理indeterminate一定是false
      //但是在此需要强制实现indeterminate=true效果
      node.indeterminate = true
    }
    //经过setIndeterminate处理,可能把checked设置成了false, 但是进来的updatedNodes全都是末级且checked=true的,所以强制true一下
    node.checked = true

    //在向上递归处理父级
    let parent = state.parentNodes[node.id]
    while (parent !== null) {
      let count = getSiblingTrueFalse(parent.children)
      totalCount = totalCount + count.totalCount
      trueCount = trueCount + count.trueCount
      falseCount = falseCount + count.falseCount
      setIndeterminate(parent, { trueCount, falseCount, totalCount })
      parent = state.parentNodes[parent.id]
    }
  }
}

// 递归将菜单及权限的checked更新
const updateChecked = (
  nodeList: TreeMenu[],
  parent: TreeMenu | null,
  ids: string[],
  funcKeys: string[],
  updatedNodes: TreeMenu[] = []
) => {
  for (let i = 0; i < nodeList.length; i++) {
    let nodePushed = false
    let node = nodeList[i]
    state.parentNodes[node.id] = parent
    if (ids.indexOf(node.id) >= 0) {
      node.checked = true
      updatedNodes.push(node)
      nodePushed = true
    }
    if (node.funcKeys && funcKeys.length > 0) {
      for (let f = 0; f < node.funcKeys.length; f++) {
        let func = node.funcKeys[f]
        if (funcKeys.indexOf(func.id) >= 0) {
          func.checked = true
          if (!nodePushed) {
            updatedNodes.push(node)
            nodePushed = true
          }
        }
      }
    }
    if (node.children) {
      updateChecked(node.children, node, ids, funcKeys, updatedNodes)
    }
  }
  //返回所有更新过的节点的父节点集合
  return updatedNodes
}

const updateAllFalse = (data: TreeMenu[]) => {
  data.forEach((node: TreeMenu) => {
    node.checked = false
    node.indeterminate = false
    node.funcKeys.forEach((f: FuncKey) => {
      f.checked = false
    })
    updateAllFalse(node.children)
  })
}
defineExpose({ updateStatus })
</script>

<style lang="scss" scoped>
.node-wrap {
  padding: 10px 0;
  &:hover {
    background-color: #f5f7fa;
    cursor: pointer;
  }
  .menu-wrap {
    display: flex;
    align-items: center;

    .arrow {
      display: flex;
      align-items: center;
      padding-right: 4px;
      color: $--color-text-placeholder;
    }
    .checkbox {
      padding-right: 6px;
    }
    .only-menu {
      display: none;
      padding: 0px 20px;
      cursor: pointer;
      color: var(--el-color-primary);
    }
  }
}
.node-wrap:hover .only-menu {
  display: inline-flex;
}
.func-wrap {
  padding: 5px 0 5px 40px;
  button {
    padding: 5px 8px;
    margin-top: 8px;
  }
  .el-button + .el-button {
    margin-left: 0px;
  }
  .el-button {
    margin-right: 10px;
  }
}

.close {
  transform: rotate(-90deg);
  transition: transform 0.2s linear;
}
.expend {
  transform: rotate(0deg);
  transition: transform 0.2s linear;
}
</style>
